﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Behaving.Composites
{
    // Takes the behaviour with the highest utility and performs that behaviour. The utility is evaluated each turn so there is no
    // hysterisis by default.
    // Hysterisis can either be added through overriding UtilitySelector with a version that keeps the previous behaviour for a time,
    // or by adding hysterisis to the Utility function of the last running behaviour.
    public class UtilitySelector : Behaviour, IHasChildren
    {
        public UtilitySelector(params BehaviourUtility[] children)
        {
            Children = children.Where(x => x != null).ToList();
        }

        public List<BehaviourUtility> Children { get; set; }
        public BehaviourUtility LastRunningBehaviour { get; set; }

        public override RunningState Execute(BehaviourTree behaviourTree)
        {
            BehaviourUtility bestBehaviourUtility = GetBestBehaviour();

            if (bestBehaviourUtility == null)
            {
                return RunningState.Failure;
            }

            RunningState runningState = bestBehaviourUtility.Behaviour.Tick(behaviourTree);
            LastRunningBehaviour = runningState == RunningState.Running ? bestBehaviourUtility : null;
            return runningState;
        }

        public IEnumerable<Behaviour> GetChildren()
        {
            return Children.Select(c => c.Behaviour);
        }

        protected virtual BehaviourUtility GetBestBehaviour()
        {
            // Override this method to create hysterisis
            return ComputeBehaviourAndGetBestBehaviour();
        }

        private BehaviourUtility ComputeBehaviourAndGetBestBehaviour()
        {
            BehaviourUtility best = null;
            foreach (BehaviourUtility behaviourUtility in Children)
            {
                behaviourUtility.ComputeUtility(LastRunningBehaviour == behaviourUtility);
                if (behaviourUtility.ComputedUtility > 0)
                {
                    if (best == null || best.ComputedUtility < behaviourUtility.ComputedUtility)
                    {
                        best = behaviourUtility;
                    }
                }
            }

            return best;
        }
    }
}